package com.gdziarmaga.webshop.core.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.gdziarmaga.webshop.utils.filtering.BaseFilter;
import com.gdziarmaga.webshop.utils.filtering.GenericFilter;


@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public class GenericDaoImpl<T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> {
   
    private Class<T> persistentClass;

    public GenericDaoImpl(final Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    
    @SuppressWarnings("unchecked")
    protected GenericDaoImpl() {
        this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

	@SuppressWarnings("unchecked")
    public PK save(T o) {
        return (PK) getHibernateTemplate().save(o);
    }

    public void update(T o) {
        getHibernateTemplate().update(o);
    }

    public void saveOrUpdate(T o) {
        getHibernateTemplate().saveOrUpdate(o);
    }

    public void delete(PK id) {
        delete(this.getById(id));
    }

    public void delete(T o) {
        getHibernateTemplate().delete(o);
    }

    public T getById(PK id) {
        return getHibernateTemplate().get(this.persistentClass, id);
    }

    public boolean exists(PK id) {
        T entity = getHibernateTemplate().get(this.persistentClass, id);
        return entity != null;
    }

    public List<T> getAll() {
        return getHibernateTemplate().loadAll(this.persistentClass);
    }

    

    @SuppressWarnings("unchecked")
    protected T getObjectByCriteria(final DetachedCriteria criteria) {
        return (T) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                return criteria.getExecutableCriteria(session).uniqueResult();
            }
        });
    }

    @SuppressWarnings("unchecked")
    protected List<T> getListByCriteria(final DetachedCriteria criteria) {
        return (List<T>) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                return criteria.getExecutableCriteria(session).list();
            }
        });
    }


//	@SuppressWarnings("unchecked")
//	public List<T> getAll(final GenericFilter filter) {
//        return (List<T>) getHibernateTemplate().execute(new HibernateCallback() {
//            public Object doInHibernate(Session session) throws HibernateException, SQLException {
//                Criteria criteria = createCriteria(session);
//                if (filter != null) {
//                    filter.addRestrictionFromFilterFields(criteria);
//                }
//                return criteria.list();
//            }
//        });
//    }
    
	protected Criteria createCriteria(Session session) {
        return session.createCriteria(this.persistentClass);
    }
	protected DetachedCriteria createDetachedCriteria() {
		return DetachedCriteria.forClass(this.persistentClass);
	}


	@Override
	public List<T> getAll(BaseFilter filter) {
		final GenericFilter genericFilter= new GenericFilter(filter);
	      return (List<T>) getHibernateTemplate().execute(new HibernateCallback() {
	            public Object doInHibernate(Session session) throws HibernateException, SQLException {
	                Criteria criteria = createCriteria(session);
	                if (genericFilter != null) {
	                    genericFilter.addRestrictionFromFilterFields(criteria);
	                }
	                return criteria.list();
	            }
	        });
	    }
    
}
